package compiler;

import java.util.ArrayList;
import static java.util.Collections.EMPTY_MAP;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import language.LArray;
import language.LArrayBuilder;
import language.LFunction;
import language.LSupplier;
import language.LType;

public abstract class Named extends LType {

    private final List list;
    private final int startIndex;
    final Map<String, LType> constants;

    Named(List list, Map<String, LType> constants) {
        this.list = new ArrayList(list);
        this.constants = constants;
        startIndex = list.get(2) instanceof Character && (Character) list.get(2) >= 'a' && (Character) list.get(2) <= 'z' ? 2 : 1;
    }

    List list() {
        return new ArrayList(list);
    }

    abstract class Instance {

        int index = startIndex;

        LType next() {
            Object obj;
            do {
                index++;
                if (index >= list.size()) {
                    return null;
                }
                obj = list.get(index);
            } while (obj.equals(' '));
            if (obj instanceof Character) {
                if (obj.equals('`')) {
                    return null;
                }
                if (obj.equals(']')) {
                    return null;
              //      return LArray.of(new Scanner(System.in).nextLine());
                }
                obj = of(obj.toString());
            } else if (obj instanceof String) {
                obj = of((String) obj);
            } else if (obj instanceof If) {
                if (next().toBoolean()) {
                    LType next = next();
                    index = ((If) obj).els;
                    obj = next;
                } else {
                    index = ((If) obj).then;
                    LType next = next();
                    index = ((If) obj).els;
                    obj = next;
                }
            } else {
                obj = (LType) obj;
            }
            while (obj instanceof Sup || obj instanceof Func) {
                if (obj instanceof LSupplier) {
                    obj = ((LSupplier) obj).get(EMPTY_MAP);
                } else if (obj instanceof NamedSupplier) {
                    obj = ((NamedSupplier) obj).call();
               // } else if (obj instanceof LocSupplier) {
                    //     obj = ((LocSupplier) obj).get(newMap());
                } else if (obj instanceof LFunction) {
                    LType next = next();
                    boolean flag = obj == constants.get("(");
                    if (next == null) {
                        if (obj instanceof LArrayBuilder) {
                            return ((LArrayBuilder) obj).asArray();
                        }
                        return (LType) obj;
                    }
                    obj = ((LFunction) obj).accept(next, EMPTY_MAP);
                    if (flag && !(obj instanceof Func)) {
                    }
                } else if (obj instanceof NamedFunction) {
                    LType next = next();
                    if (next == null) {
                        if (obj instanceof LArrayBuilder) {
                            return ((LArrayBuilder) obj).asArray();
                        }
                        return (LType) obj;
                    }
                    obj = ((NamedFunction) obj).call(next);
                } else if (obj instanceof LocFunction) {
                    LType next = next();
                    if (next == null) {
                        if (obj instanceof LArrayBuilder) {
                            return ((LArrayBuilder) obj).asArray();
                        }
                        if (Named.this instanceof NamedFunction) {
                            NamedFunction ths = (NamedFunction) Named.this;
                            return ((LocFunction) obj).setVariable(ths.variable, ((NamedFunction.Instance)this).input);
                        }
                    }
                    obj = ((LocFunction) obj).accept(next, newMap());
                }
            }
            return (LType) obj;
        }

        abstract Object of(String s);

        abstract Map<Character, LType> newMap();
    }
}
